package server;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;

public class GameThread extends Thread{
	
	private boolean gameStarted = false;
	private boolean countingDown = false;
	private Hashtable<String,Player> activePlayers;
	private Hashtable<String,Player> playertable;
	private Hashtable<String,Answer> playerAnswers = new Hashtable<String,Answer>(Game.MAX_NUMBER_OF_PLAYERS+1,1f);  
	private ArrayList<Question> questionBank;
	private Question currentQuestion = new Question("",null,"");
	
	/**
	 * Initialize a new game.
	 * @param activePlayers the players who should be in the game.
	 * @param questionBank questions to be used in the game.
	 */
	public GameThread(Hashtable<String,Player> playertable, Hashtable<String,Player> activePlayers, ArrayList<Question> questionBank){
		super();
		this.playertable = playertable;
		this.activePlayers = activePlayers;
		this.questionBank = questionBank;
		for (Enumeration<Player> e = activePlayers.elements(); e.hasMoreElements(); ){
			e.nextElement().resetAnswers(); 
		}
	}
	
	/**
	 * Start the game
	 */
	public void run(){
		
		if (questionBank.size() < Game.QUESTIONS_PER_GAME)
			Game.readQuestionFile();
		
		countingDown = true;
		sendToAllPlayers("A new game will now commence.");
		
		for (int i = 10; i>0 && countingDown; i--) {
			sendToAllPlayers("Game starts in "+i+" seconds.");
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				//e.printStackTrace();
			}
		}
		
		/*
		 * if countingDown is false, the game have been ended. The while
		 * loop should therefore not be entered.
		 */
		if (countingDown) { 
			gameStarted = true; // New players will be queued from now on.
			countingDown = false;
		}
		
		/*
		 * While the game has not been interrupted 
		 * and the maximum number of questions have not been asked
		 */
		int questionsAsked = 0;
		while (gameStarted && questionsAsked < Game.QUESTIONS_PER_GAME) {
			
			currentQuestion = questionBank.remove(0);
			sendToAllPlayers(currentQuestion.getQuestion());
			
			/*
			 * Sleep until all players have answered
			 */
			try {
				sleep(Game.MAX_TIME_PER_QUESTION);
			} catch (InterruptedException e) {
				Server.consolePrint("Players finished question before timeout.");
			}
			
			/*
			 * Go through all players and validate the answers
			 */
			for (Enumeration<Player> e = activePlayers.elements(); e.hasMoreElements(); ){
				Player p = e.nextElement();
				Answer a = playerAnswers.remove(p.getUsername());
				/*
				 * If the player haven't answered, a == null
				 */
				if (a != null) {
					long timeUsedOnThisQuestion = a.getTime() - currentQuestion.getTime();
					
					if ( currentQuestion.validateAnswer(a) ){
						p.addCorrectAnswer();
						p.socketThread.send("You answered correct in "+timeUsedOnThisQuestion/1000.0+" seconds.");
					} else {
						p.socketThread.send("Your answer was not correct.");
					}
					
					p.addTime(timeUsedOnThisQuestion);
				} else {
					p.socketThread.send("Sorry. You didn't make it.");
					p.addTime(Game.MAX_TIME_PER_QUESTION);
				}
				
			}
			
			/*
			 * Send statistics to all players.
			 */
			sendHighscoresToAllPlayers("Current highscores:");
			
			playerAnswers.clear();
			questionsAsked++;
		}
		
		/*
		 * Check if the game ended because all questions were asked.
		 * It could have ended because a player left.
		 */
		if (questionsAsked >= Game.QUESTIONS_PER_GAME) {
			
			ArrayList<Player> sortedPlayers = getPlayersHighscoreSorted();
			
			sendToAllPlayers("And the winner was: " + sortedPlayers.get(0).getUsername());
			sortedPlayers.get(0).socketThread.send("Congratulations!");
			sendHighscoresToAllPlayers("Final highscores:");
			sendToAllPlayers("\nA new game will begin in 3 seconds.");
			
			try {
				sleep(3000);
			} catch (InterruptedException e) {}
			
			sendToAllPlayers("\n\n\n");
			Game.startNewGame();
		} else {
			sendToAllPlayers("Game ended too early.");
		}
	}
	
	/**
	 * Makes the current game end.
	 */
	public void stopGame() {
		gameStarted = false;
		countingDown = false;
		this.interrupt();
	}
	
	/**
	 * Returns true if the game have been started, but is still counting down.
	 * @return see above.
	 */
	public boolean isCountingDown() {
		return countingDown;
	}
	
	/**
	 * Returns true if the game is running, i.e., after the countdown.
	 * @return see above.
	 */
	public boolean isGameStarted() {
		return gameStarted;
	}
	
	/**
	 * Sends a message to all active players.
	 * @param s message to send.
	 */
	public void sendToAllPlayers(String s) {
		
		for (Enumeration<Player> e = playertable.elements(); e.hasMoreElements();) {
			Player p = e.nextElement();
			p.socketThread.send(s);
		}
	}
	
	/**
	 * Registers an answer from a player, if the player have not already answered.
	 * @param username username of the player who answered.
	 * @param answer answer from the player.
	 */
	public void processPlayerAnswer(String username, String answer) {
		
		if (gameStarted) {
			/*
			 * Register the answer if the player have not answered before.
			 */
			if (!playerAnswers.containsKey(username)) {
				playerAnswers.put(username, new Answer(answer));
				sendToAllPlayers(username + " have answered.");
			}
			
			if (playerAnswers.size() >= activePlayers.size()){
				this.interrupt(); // Stop the 15 sec "timer"
			}
		}
	}

	private ArrayList<Player> getPlayersHighscoreSorted(){
		ArrayList<Player> a = new ArrayList<Player>();
		
		for (Enumeration<Player> e = activePlayers.elements(); e.hasMoreElements(); )
			a.add(e.nextElement());
		
		Collections.sort(a);
		return a;
	}
	
	private void sendHighscoresToAllPlayers(String title){
		
		String message = "";
		if (title != null && !title.trim().isEmpty())
			message = "\n"+title+"\n";
		
		ArrayList<Player> tempPlayerList = getPlayersHighscoreSorted();
		
		for (Player p : tempPlayerList) {
			message += p.getUsername()+": " +p.getCorrectAnswers()+" correct answers in " +p.getTime()/1000.0+" seconds.\n";
		}
		
		sendToAllPlayers(message);
		
	}
}
